package com.life.ai.server.common.exception;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import cn.hutool.core.collection.CollUtil;
import com.life.ai.server.common.domain.R;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.MyBatisSystemException;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.server.MethodNotAllowedException;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;

import static com.life.ai.server.common.exception.errorcode.BaseErrorCode.*;

/**
 * 全局异常处理器
 *
 * @author kylewka
 */
@Slf4j
@RestControllerAdvice
@SuppressWarnings("all")
public class GlobalExceptionHandler {

    /**
     * 客户操作异常
     *
     * @param e 捕获的异常
     * @return 异常错误码和异常消息
     */
    @ExceptionHandler(value = ClientException.class)
    public R<Object> handleException(ClientException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',客户操作异常'{}'", requestURI, e.getMessage());
        return R.fail(e.getErrorCode(), e.getMessage());
    }

    /**
     * 系统服务异常
     *
     * @param e 捕获的异常
     * @return 异常错误码和异常消息
     */
    @ExceptionHandler(value = ServiceException.class)
    public R<Object> handleException(ServiceException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',系统服务异常'{}'", requestURI, e.getMessage());
        return R.fail(e.getErrorCode(), e.getMessage());
    }

    /**
     * 远程服务调用异常
     *
     * @param e 捕获的异常
     * @return 异常错误码和异常消息
     */
    @ExceptionHandler(value = RemoteException.class)
    public R<Object> handleException(RemoteException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',远程服务调用异常'{}'", requestURI, e.getMessage());
        return R.fail(e.getErrorCode(), e.getMessage());
    }

    /**
     * 未登录或登录失效
     *
     * @param e 捕获的异常
     * @return 异常错误码和异常消息
     */
    @ExceptionHandler(value = NotLoginException.class)
    public R<Object> handleException(NotLoginException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',未登录或无效认证'{}'", requestURI, e.getMessage());
        return R.fail(USER_AUTH_EXPIRED);
    }


    /**
     * 未登录或登录失效
     *
     * @param e 捕获的异常
     * @return 异常错误码和异常消息
     */
    @ExceptionHandler(value = NotPermissionException.class)
    public R<Object> handleException(NotPermissionException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',权限码校验失败'{}'", requestURI, e.getMessage());
        return R.fail(USER_PERMIT_ERROR);
    }

    /**
     * 角色权限异常
     */
    @ExceptionHandler(NotRoleException.class)
    public R<Void> handleException(NotRoleException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',角色权限校验失败'{}'", requestURI, e.getMessage());
        return R.fail(USER_PERMIT_ERROR);
    }


    /**
     * 请求参数错误
     *
     * @param e 捕获的异常
     * @return 异常错误码和异常消息
     */
    @ExceptionHandler(value = HttpMessageConversionException.class)
    public R<Object> handleException(HttpMessageConversionException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',请求体格式错误'{}'", requestURI, e.getMessage());
        return R.fail(REQ_PARAMS_ERROR);
    }

    /**
     * 请求参数错判
     *
     * @param e 捕获的异常
     * @return 异常错误码和异常消息
     */
    @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
    public R<Object> handleException(HttpRequestMethodNotSupportedException e,
                                     HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod());
        return R.fail(REQ_METHOD_ERROR);
    }

    /**
     * 请求参数错判
     *
     * @param e 捕获的异常
     * @return 异常错误码和异常消息
     */
    @ExceptionHandler(value = MethodNotAllowedException.class)
    public ResponseEntity<Void> handleException(MethodNotAllowedException e,
                                                HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.warn("请求地址'{}',{}", requestURI, e.getMessage());
        return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED).build();
    }

    /**
     * 请求路径中缺少必需的路径变量
     */
    @ExceptionHandler(MissingPathVariableException.class)
    public R<Void> handleException(MissingPathVariableException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',请求路径中缺少必需的路径变量'{}'", requestURI, e);
        return R.fail(REQ_PATH_ERROR);
    }

    /**
     * 请求参数类型不匹配
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public R<Void> handleException(MethodArgumentTypeMismatchException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',请求参数类型不匹配，参数'{}'要求类型为：'{}'，但输入值为：'{}'", requestURI, e.getName(), e.getRequiredType().getName(), e.getValue());
        return R.fail(REQ_PARAMS_ERROR);
    }


    /**
     * 参数校验异常
     *
     * @param e 捕获的异常
     * @return 异常错误码和异常消息
     */
    @ExceptionHandler(value = {MethodArgumentNotValidException.class})
    public R<Object> handleException(MethodArgumentNotValidException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',参数校验异常.", requestURI, e);
        String message = e.getBindingResult().getFieldError().getDefaultMessage();
        return R.fail(REQ_PARAMS_ERROR, message);
    }

    /**
     * 当 @Validated 修饰单个参数时（此时 @Validated 在类上而不是在方法中），
     * 会抛出ConstraintViolationException异常。
     *
     * @param ex 捕获的异常
     * @return 异常错误码和异常消息
     */
    @ExceptionHandler({ConstraintViolationException.class, IllegalArgumentException.class})
    public R<Object> handleException(ConstraintViolationException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',参数校验异常.", requestURI, e);
        String message = CollUtil.join(e.getConstraintViolations(), ",", ConstraintViolation::getMessage);
        return R.fail(REQ_PARAMS_ERROR);
    }

    /**
     * 处理参数校验异常
     *
     * @param e 捕获的异常
     * @return 异常错误码和异常消息
     */
    @ExceptionHandler(value = BindException.class)
    public R<Object> handleException(BindException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',参数校验异常.", requestURI, e);
        String message = CollUtil.join(e.getAllErrors(), ",", DefaultMessageSourceResolvable::getDefaultMessage);
        return R.fail(REQ_PARAMS_ERROR, message);
    }

    /**
     * Mybatis系统异常 通用处理
     */
    @ExceptionHandler(MyBatisSystemException.class)
    public R<Void> handleException(MyBatisSystemException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        String message = e.getMessage();
        if (message.contains("CannotFindDataSourceException")) {
            log.error("请求地址'{}', 未找到数据源", requestURI);
            return R.fail("未找到数据源，请联系管理员确认");
        }
        log.error("请求地址'{}', Mybatis系统异常", requestURI, e);
        return R.fail(message);
    }

    /**
     * 文件上传过大异常
     */
    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public R<Void> handleException(MaxUploadSizeExceededException e) {
        log.error(e.getMessage(), e);
        return R.fail("上传文件不可大于10M");
    }

    /**
     * 服务器内部异常,非上面异常时由当前方法处理返回
     *
     * @param e 捕获的异常
     * @return 异常错误码和异常消息
     */
    @ExceptionHandler(value = Exception.class)
    public R<Object> handleException(Exception e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',系统未知异常.", requestURI, e);
        return R.fail(SERVICE_ERROR);
    }
}
